// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// kmac_pkg

package kmac_pkg;
  parameter int MsgWidth = sha3_pkg::MsgWidth;
  parameter int MsgStrbW = sha3_pkg::MsgStrbW;

  // Message FIFO depth
  //
  // Assume entropy is ready always (if Share is reused as an entropy in Chi)
  // Then it takes 72 cycles to complete the Keccak round. While Keccak is in
  // operation, the module need to store the incoming messages to not degrade
  // the throughput.
  //
  // Based on the observation from HMAC case, the core usually takes 5 clocks
  // to fetch data and store into KMAC. So the core can push at most 14.5 X 4B
  // which is 58B. After that, Keccak can fetch the data from MSG_FIFO faster
  // rate than the core can push. To fetch 58B, it takes around 7~8 cycles.
  // For that time, the core only can push at most 2 DW. After that Keccak
  // waits the incoming message.
  //
  // So Message FIFO doesn't need full block size except the KMAC case, which
  // is delayed the operation by processing Function Name N, customization S,
  // and secret keys. But KMAC doesn't need high throughput anyway (72Mb/s).
  parameter int RegIntfWidth = 32; // 32bit interface
  parameter int RegLatency   = 5;  // 5 cycle to write one Word
  parameter int Sha3Latency  = 72; // Expected masked sha3 processing time 24x3

  // Total required buffer size while SHA3 is in processing
  parameter int BufferCycles   = (Sha3Latency + RegLatency - 1)/RegLatency;
  parameter int BufferSizeBits = RegIntfWidth * BufferCycles;

  // Required MsgFifoDepth. Adding slightly more buffer for margin
  parameter int MsgFifoDepth   = 2 + ((BufferSizeBits + MsgWidth - 1)/MsgWidth);
  parameter int MsgFifoDepthW  = $clog2(MsgFifoDepth+1);

  parameter int MsgWindowWidth = 32; // Register width
  parameter int MsgWindowDepth = 512; // 2kB space

  // Key related definitions
  // If this value is changed, please modify the logic inside kmac_core
  // that assigns the value into `encoded_key`
  parameter int MaxKeyLen = 512;

  // size of encode_string(Key)
  // $ceil($clog2(MaxKeyLen+1)/8)
  parameter int MaxEncodedKeyLenW = $clog2(MaxKeyLen+1);
  parameter int MaxEncodedKeyLenByte = (MaxEncodedKeyLenW + 8 - 1) / 8;
  parameter int MaxEncodedKeyLenSize = MaxEncodedKeyLenByte * 8;

  //                             Secret Key  left_encode(len(Key))
  //                             ----------  ------------------------
  parameter int MaxEncodedKeyW = MaxKeyLen + MaxEncodedKeyLenSize + 8;

  // key_len is SW configurable CSR.
  // Current KMAC allows 5 key length options.
  // This value determines the KMAC core how to map the value
  // from Secret Key register to key size block
  typedef enum logic [2:0] {
    Key128 = 3'b 000, // 128 bit secret key
    Key192 = 3'b 001, // 192 bit secret key
    Key256 = 3'b 010, // 256 bit secret key
    Key384 = 3'b 011, // 384 bit secret key
    Key512 = 3'b 100  // 512 bit secret key
  } key_len_e;


  // kmac_cmd_e defines the possible command sets that software issues via
  // !!CMD register. This is mainly to limit the error scenario that SW writes
  // multiple commands at once.
  typedef enum logic [3:0] {
    CmdNone      = 4'b 0000,
    CmdStart     = 4'b 0001,
    CmdProcess   = 4'b 0010,
    CmdManualRun = 4'b 0100,
    CmdDone      = 4'b 1000
  } kmac_cmd_e;

  // Timer
  parameter int unsigned TimerPrescalerW = 10;
  parameter int unsigned EdnWaitTimerW   = 16;

  // Entropy Mode Selection : Should be matched to register package Enum value
  typedef enum logic [1:0] {
    EntropyModeNone = 2'h 0,
    EntropyModeEdn  = 2'h 1,
    EntropyModeSw   = 2'h 2
  } entropy_mode_e;

  // entropy lfsr related
  parameter int unsigned EntropyLfsrW = 64;
  typedef logic [EntropyLfsrW-1:0][$clog2(EntropyLfsrW)-1:0] lfsr_perm_t;
  parameter lfsr_perm_t RndCnstLfsrPermDefault = {
    128'h810970222da1b1b1187551c3ff94574a,
    256'h970d171aa41948cbe3a58167d3b47c268acfcbb2fa627b9c0a2fdf578f4ed32b
  };

  ///////////////////////////
  // Application interface //
  ///////////////////////////

  // Number of the application interface
  // Currently KMAC has three interface.
  // 0: KeyMgr
  // 1: LC_CTRL
  // 2: ROM_CTRL
  // Make sure to change `width` of app inter-module signal definition
  // if this value is changed.
  parameter int unsigned NumAppIntf = 3;

  // Application Algorithm
  // Each interface can choose algorithms among SHA3, cSHAKE, KMAC
  typedef enum bit [1:0] {
    // SHA3 mode doer not nees any additional information.
    // Prefix will be tied to all zero and not used.
    AppSHA3   = 0,

    // In CShake/ KMAC mode, the Prefix can be determined by the compile-time
    // parameter or through CSRs.
    AppCShake = 1,

    // In KMAC mode, the secret key always comes from sideload.
    AppKMAC   = 2
  } app_mode_e;

  // Predefined encoded_string
  parameter logic [15:0] EncodedStringEmpty = 16'h 0001;
  parameter logic [47:0] EncodedStringKMAC = 48'h 4341_4D4B_2001;
  parameter int unsigned NSPrefixW = sha3_pkg::NSRegisterSize*8;

  typedef struct packed {
    app_mode_e Mode;

    sha3_pkg::keccak_strength_e Strength;

    // PrefixMode determines the origin value of Prefix that is used in KMAC
    // and cSHAKE operations.
    // Choose **0** for CSRs (!!PREFIX), or **1** to use `Prefix` parameter
    // below.
    bit PrefixMode;

    // If `PrefixMode` is 1'b 1, then this `Prefix` value will be used in
    // cSHAKE or KMAC operation.
    logic [NSPrefixW-1:0] Prefix;
  } app_config_t;

  parameter app_config_t AppCfg [NumAppIntf] = '{
    // KeyMgr
    '{
      Mode:       AppKMAC, // KeyMgr uses KMAC operation
      Strength:   sha3_pkg::L256,
      PrefixMode: 1'b 0,   // Use CSR for prefix
      Prefix:     '0       // Not used in CSR prefix mode
    },

    // LC_CTRL
    '{
      Mode:       AppCShake,
      Strength:   sha3_pkg::L128,
      PrefixMode: 1'b 1,     // Use prefix parameter
      // {fname: encode_string(""), custom_str: encode_string("LC_CTRL")}
      Prefix: NSPrefixW'(88'h 4c_5254_435f_434C_3801_0001)
    },

    // ROM_CTRL
    '{
      Mode:       AppCShake,
      Strength:   sha3_pkg::L256,
      PrefixMode: 1'b 1,     // Use prefix parameter
      // {fname: encode_string(""), custom_str: encode_string("ROM_CTRL")}
      Prefix: NSPrefixW'(96'h 4c52_5443_5f4d_4f52_4001_0001)
    }
  };

  // Exporting the app internal mux selection enum into the package. So that DV
  // can use this enum in its scoreboard.
  typedef enum logic [2:0] {
    SelNone   = 3'b 000,
    SelApp    = 3'b 101,
    SelOutLen = 3'b 110,
    SelSw     = 3'b 010
  } app_mux_sel_e ;



  // MsgWidth : 64
  // MsgStrbW : 8
  parameter int unsigned AppDigestW = 384;
  parameter int unsigned AppKeyW = 256;

  typedef struct packed {
    logic valid;
    logic [MsgWidth-1:0] data;
    logic [MsgStrbW-1:0] strb;
    // last indicates the last beat of the data. strb can be partial only with
    // last.
    logic last;
  } app_req_t;

  typedef struct packed {
    logic ready;
    logic done;
    logic [AppDigestW-1:0] digest_share0;
    logic [AppDigestW-1:0] digest_share1;
    // Error is valid when done is high. If any error occurs during KDF, KMAC
    // returns the garbage digest data with error. The KeyMgr discards the
    // digest and may re-initiate the process.
    logic error;
  } app_rsp_t;

  parameter app_req_t APP_REQ_DEFAULT = '{
    valid: 1'b 0,
    data: '0,
    strb: '0,
    last: 1'b 0
  };
  parameter app_rsp_t APP_RSP_DEFAULT = '{
    ready: 1'b1,
    done:  1'b1,
    digest_share0: AppDigestW'(32'hDEADBEEF),
    digest_share1: AppDigestW'(32'hFACEBEEF),
    error: 1'b1
  };


  ////////////////////
  // Error Handling //
  ////////////////////

  // Error structure is same to the SHA3 one. The codes do not overlap.
  typedef enum logic [7:0] {
    ErrNone = 8'h 00,

    // ErrSha3SwControl occurs when software sent wrong flow signal.
    // e.g) Sw set `process_i` without `start_i`. The state machine ignores
    //      the signal and report through the error FIFO.
    //ErrSha3SwControl = 8'h 80

    // ErrKeyNotValid: KeyMgr interface raises an error if the secret key is
    // not valid when KeyMgr initiates KDF.
    ErrKeyNotValid = 8'h 01,

    // ErrSwPushMsgFifo: Sw writes data into Msg FIFO abruptly.
    // This error occurs in below scenario:
    //   - Sw does not send "Start" command to KMAC then writes data into
    //     Msg FIFO
    //   - Sw writes data into Msg FIFO when KeyMgr is in operating
    ErrSwPushedMsgFifo = 8'h 02,

    // ErrSwIssuedCmdInAppActive
    //  - Sw writes any command while AppIntf is in active.
    ErrSwIssuedCmdInAppActive = 8'h 03,

    // ErrWaitTimerExpired
    // Entropy Wait timer expired. Something wrong on EDN i/f
    ErrWaitTimerExpired = 8'h 04,

    // ErrIncorrectEntropyMode
    // Incorrect Entropy mode when entropy is ready
    ErrIncorrectEntropyMode = 8'h 05,

    // ErrUnexpectedModeStrength
    ErrUnexpectedModeStrength = 8'h 06,

    // ErrIncorrectFunctionName "KMAC"
    ErrIncorrectFunctionName = 8'h 07,

    // ErrSwCmdSequence
    ErrSwCmdSequence = 8'h 08
  } err_code_e;

  typedef struct packed {
    logic        valid;
    err_code_e   code; // Type of error
    logic [23:0] info; // Additional Debug info
  } err_t;

  ///////////////////////
  // Library Functions //
  ///////////////////////

  // Endian conversion functions (32-bit, 64-bit)
  function automatic logic [31:0] conv_endian32( input logic [31:0] v, input logic swap);
    logic [31:0] conv_data = {<<8{v}};
    conv_endian32 = (swap) ? conv_data : v ;
  endfunction : conv_endian32

  function automatic logic [63:0] conv_endian64( input logic [63:0] v, input logic swap);
    logic [63:0] conv_data = {<<8{v}};
    conv_endian64 = (swap) ? conv_data : v ;
  endfunction : conv_endian64

endpackage : kmac_pkg
